import time

import smbus2 as smbus

# commands
LCD_CLEAR_DISPLAY = 0x01
LCD_RETURN_HOME = 0x02
LCD_ENTRY_MODE_SET = 0x04
LCD_DISPLAY_CONTROL = 0x08
LCD_CURSOR_SHIFT = 0x10
LCD_FUNCTION_SET = 0x20
LCD_SET_CGRAM_ADDR = 0x40
LCD_SET_DDRAM_ADDR = 0x80

# flags for display entry mode
LCD_ENTRY_LEFT = 0x00
LCD_ENTRY_RIGHT = 0x02
LCD_ENTRY_SHIFT_ON = 0x01
LCD_ENTRY_SHIFT_OFF = 0x00

# flags for display on/off control
LCD_DISPLAY_ON = 0x04
LCD_DISPLAY_OFF = 0x00
LCD_CURSOR_ON = 0x02
LCD_CURSOR_OFF = 0x00
LCD_BLINK_ON = 0x01
LCD_BLINK_OFF = 0x00

# flags for display/cursor shift
LCD_DISPLAY_MOVE = 0x08
LCD_CURSOR_MOVE = 0x00
LCD_MOVE_RIGHT = 0x04
LCD_MOVE_LEFT = 0x00

# flags for function set
LCD_8BIT_MODE = 0x10
LCD_4BIT_MODE = 0x00
LCD_2LINE = 0x08
LCD_1LINE = 0x00
LCD_5x10_DOTS = 0x04
LCD_5x7_DOTS = 0x00

# flags for backlight control
LCD_BACK_LIGHT = 0x08
LCD_NO_BACK_LIGHT = 0x00

En = 0b00000100  # Enable bit
Rw = 0b00000010  # Read/Write bit
Rs = 0b00000001  # Register select bit


# Rs == 1 写数据    Rs == 0 写指令

# 四位模式:  7 6 5 4 |  3  |  2 |  1 |  0
#          指令/数据 | 背光 | EN | Rw | Rs
class LCD:
    # initializes objects and lcd
    def __init__(self, address, port):
        self.addr = address
        self.bus = smbus.SMBus(port)
        # 两次初始化8位模式 0x33
        self.lcd_write_cmd(0x33)
        time.sleep(.005)
        # 初始化8位模式+初始化4位模式
        self.lcd_write_cmd(0x32)
        time.sleep(.005)
        self.lcd_write_cmd(LCD_FUNCTION_SET | LCD_2LINE | LCD_5x7_DOTS | LCD_4BIT_MODE)
        time.sleep(.005)
        self.lcd_write_cmd(LCD_DISPLAY_CONTROL | LCD_DISPLAY_ON)
        time.sleep(.005)
        self.lcd_write_cmd(LCD_CLEAR_DISPLAY)
        time.sleep(.005)
        self.lcd_write_cmd(LCD_ENTRY_MODE_SET | LCD_ENTRY_RIGHT)
        self.bus.write_byte(address, LCD_DISPLAY_CONTROL)

    def _lcd_write_four_bits_(self, data):
        self.bus.write_byte(self.addr, data | En | LCD_BACK_LIGHT)
        time.sleep(.005)
        self.bus.write_byte(self.addr, (data & ~En) | LCD_BACK_LIGHT)

    def _lcd_write_(self, cmd, mode):
        self._lcd_write_four_bits_(mode | (cmd & 0xF0))
        self._lcd_write_four_bits_(mode | ((cmd << 4) & 0xF0))

    def lcd_write_cmd(self, cmd):
        self._lcd_write_(cmd, 0)

    def lcd_write_data(self, cmd):
        self._lcd_write_(cmd, 1)

    def lcd_display_string(self, string, line=1, pos=0):
        if line == 1:
            pos = 0x00 + pos
        elif line == 2:
            pos = 0x40 + pos

        self.lcd_write_cmd(0x80 + pos)

        for char in string:
            self.lcd_write_data(ord(char))

    def lcd_clear(self):
        self.lcd_write_cmd(LCD_CLEAR_DISPLAY)
        self.lcd_write_cmd(LCD_RETURN_HOME)


if __name__ == '__main__':
    lcd_addr = 0x27
    i2c_port = 1
    my_lcd = LCD(lcd_addr, i2c_port)
    my_lcd.lcd_clear()
    s = "zhao nina, zhaosihui, you are a fool!!!"
    for i in range(len(s) - 16 + 1):
        my_lcd.lcd_display_string(s[i:i + 16], 1)
        time.sleep(1)
        my_lcd.lcd_clear()
